import md5;
import socket;
from configuration import yaml_configuration;
#from configfile import *;
import configfile;
from Crypto.Cipher import AES;
import base64;
import os;
import yaml
from msg import Message
import copy
import threading
import string
import time

#a = getForwardingTable('dick', 'butt')
#b = getForwardingTable('boobs', 'tits')

# the block size for the cipher object; must be 16, 24, or 32 for AES
#BLOCK_SIZE = 32

# the character used for padding--with a block cipher such as AES, the value
# you encrypt must be a multiple of BLOCK_SIZE in length.  This character is
# used to ensure that your value is always a multiple of BLOCK_SIZE
#PADDING = '\x00'

# one-liner to sufficiently pad the text to be encrypted
#pad = lambda s: s + (BLOCK_SIZE - len(s) % BLOCK_SIZE) * PADDING

# one-liners to encrypt/encode and decrypt/decode a string
# encrypt with AES, encode with base64
#EncodeAES = lambda c, s: base64.b64encode(c.encrypt(pad(s)))
#EncodeAES = lambda c, s: (c.encrypt(pad(s)))
#DecodeAES = lambda c, e: c.decrypt(base64.b64decode(e)).rstrip(PADDING)
#DecodeAES = lambda c, e: c.decrypt((e)).rstrip(PADDING)

#secret = pad('tartans@1')
#print secret
# create a cipher object using the random secret
#cipher = AES.new(secret)
#print cipher
# encode a string
#encoded = EncodeAES(cipher, 'login;andrew;longerpassword')
#print 'Encrypted string:', encoded

# decode the encoded string
#decoded = DecodeAES(cipher, encoded)
#print 'Decrypted string:', decoded


def get_ip_port():
    global config
    res = ''
    for name in config:
        res = res + '%' + name['name'] + ';'
        res = res + name['ip'] + ';'
        res = res + str(name['port'])
    res = res + '%'
    return res


def register_user(userName, password, fName, lName, group):
    print "register user!!!!!"
    if userName in userList:
        return "TAKEN"
    un = userName
    #un = string.ascii_lowercase(userName)
    if(un == 'student' or un == 'faculty' or un == 'staff' or un == 'guest'):
        return "TAKEN"
    userList[userName] = [password, fName, lName, group]
    return "ACK"

def get_cookie(user, password):
    global cthread
    if(user not in userList):
        return "INVALID"
    if(userList[user][0] != password):
        return "INVALID"
    m = md5.new()
    m.update(user)
    m.update("1cookies!" + str(cthread.get_counter()))
    return m.hexdigest()

def test_cookie (user, cookie) :
    global cthread
    m = md5.new()
    m.update(user)
    m.update('1cookies!' + str(cthread.get_counter()))
    c = m.hexdigest()
    if(cookie == c):
        return "GOOD_COOKIE"
    else:
        return "BAD_COOKIE"

def get_user_groups(user):
    if(user not in userList):
        return "INVALID"
    return userList[user][3]


def remove_edge_from_config (src, dst):
    global init
    global config
    flag = False

    srcItem = {}
    srcCount = 0
    for item in config :
        if(src == item['name']):
            neighborsSrc = item['group']
            srcItem = item
            flag = True
            break
        srcCount = srcCount + 1
    if flag == False :
        return "Error"

    destItem = {}
    destCount = 0
    flag = False
    for item in config :
        if(dst == item['name']):
            neighborsDest = item['group']
            destItem = item
            flag = True
            break
        destCount = destCount + 1
    if flag == False :
        return "Error"

    if(dst in neighborsSrc):
        neighborsSrc.remove(dst)
        #print 'removing ',
        #print neighbors
        if len(neighborsSrc) == 1:
            #print 'deleting dst %s : ' % (src)
            #config.remove(srcItem)
            config.pop(srcCount)
            #print "del 1 comGraph is : ",
            #print comGraph
        else :
            i = 0
            for item in config :
                if src == item['name'] :
                    item['group'] = neighborsSrc
                    config[i]['group'] = neighborsSrc
                i = i + 1


    if(src in neighborsDest):
        neighborsDest.remove(src)
        #print 'removing ',
        #print neighbors
        if len(neighborsDest) == 1:
            #print 'deleting dst %s : ' % (src)
            #config.remove(destItem)
            config.pop(destCount)
            #print "del 1 comGraph is : ",
            #print comGraph
        else :
            i = 0
            for item in config :
                if dst == item['name'] :
                    item['group'] = neighborsDest
                    config[i]['group'] = neighborsDest
                i = i + 1
    print "after config is : ",
    print config
    return "Success"

def get_user_metadata(user):
    if(user not in userList):
        return "INVALID"
    else:
        return userList[user][1] + "," + userList[user][2] + "," + userList[user][3] + ','

class cookie_thread(threading.Thread):
    def __init__(self, count):
        threading.Thread.__init__(self)
        self.count = count

    def get_counter(self):
        return self.count

    def run(self):
        time.sleep(20)
        self.count = self.count + 1


userList = {}
#start the cookie thread
cthread = cookie_thread(1)

# BEGIN DEBUG CODE
register_user('a', 'a', 'Travis', 'Safford', 'student')
register_user('b', 'b', 'Kishy', 'Kumar', 'student')
register_user('c', 'c', 'Lin', 'Qi', 'student')
register_user('d', 'd', 'Andrew', 'Santell', 'student')
register_user('e', 'e', 'e', 'e', 'student')
register_user('f', 'f', 'f', 'f', 'student')
register_user('g', 'g', 'g', 'g', 'student')

#print userList['travis']

#print get_cookie('a', 'a')
#print get_cookie('b', 'b')
#print get_cookie('c', 'c')

# END DEBUG CODE

#get yaml information
configFile = configfile.yaml_configuration('demo.yaml') #TODO update this to a url?
config = configFile.getConfiguration()


copied_namelist = []

config_copy = []
config_copy = copy.deepcopy(config)

for item in config_copy :
    copied_namelist.append(item['name'])

namelist = []
for item in config :
    namelist.append(item['name'])

'''
print configfile.nameIpPorts

configfile.getForwardingTable('demo.yaml', 'a')
print '----------'
configfile.get_graph()
print '----------'
print configfile.ipList

configfile.new_node('billy', "192.168.1.1", 12345, ['a', 'b'])
configfile.get_graph()
'''

#configfile.getForwardingTable('demo.yaml', 'a')
#end debug code
#Create a socket and listen on port 23456
srvSock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srvSock.bind(('', 23456))
srvSock.listen(5)

while(1):
    #break
    cliSock, cliAddr = srvSock.accept()
    msg = cliSock.recv(1024)
    #print "Message = %s" % msg
    #test = DecodeAES(cipher, msg)
    #print test
    #print 'decrypted: ' + DecodeAES(cipher, msg)
    t = msg.split(";")
    print "Request : ",
    print t
    if(t[0] == "register_user"):
        if(len(t) < 2):
            cliSock.send("NACK")
        else:
            dec = base64.b64decode(t[1])
            print dec
            sp = dec.split(';')
            print sp
            if(len(sp) < 5):
                cliSock.send("NACK")
            else:
                ret = register_user(sp[0], sp[1], sp[2], sp[3], sp[4])
                print ret
                cliSock.send(ret)
    #elif (t[0] == 'test_cookie'):
    elif (t[0] == 'test_cookie'):
        if(len(t) < 3):
            cliSock.send("NACK")
        else:
            res = test_cookie(t[1],t[2])
            cliSock.send(res)
    elif (t[0] == 'get_cookie'):
        if(len(t) < 3):
            cliSock.send("NACK")
        else:
            ret = get_cookie(t[1], t[2])
            cliSock.send(ret)

    elif(t[0] == 'get_user_groups'):
        if(len(t) < 2):
            cliSock.send("NACK")
        else:
            cliSock.send(get_user_groups(t[1]))

    elif(t[0] == 'get_user_metadata'):
        if(len(t) < 2):
            cliSock.send("NACK")
        else:
            cliSock.send(get_user_metadata(t[1]))

    elif(t[0] == 'login'):
        if(len(t) < 2):
            cliSock.send("NACK")
        else:
            dec = base64.b64decode(t[1])
            sp = dec.split(';')
            print sp
            print dec
            if(len(sp) < 2):
                cliSock.send("NACK")
            else:
                ret = get_cookie(sp[0], sp[1])
                if(ret == "INVALID"):
                    cliSock.send(ret)
                else:
                    print ret + get_ip_port()
                    cliSock.send(ret + get_ip_port())
    elif(t[0] == 'get_config'):

        #initial = 0
        if(len(t) < 2):
            cliSock.send("NACK")
        else :
            nodeName = t[1]
            flag = False
            print "namelist is : ",
            print namelist

            if nodeName not in copied_namelist :
                cliSock.send("error")
                continue

            if nodeName in namelist:
                namelist.remove(nodeName)
                flag = True



            if flag == True :
                cliSock.send(yaml.dump(config))

            else :
            # add the node into the graph

                print "Config copy : ",
                print config_copy

                for item in config_copy :
                    print "item name is : " + item['name']
                    if nodeName == item['name'] :
                        config.append(item)
                        data = yaml.dump(item)


                print "Auth new config after addition ",
                print config

                #send the messages to other nodes
                print "to be inserted : ",
                print data
                for item in config :
                    if item['name'] != nodeName :
                        item['group'].append(nodeName)
                        s=socket.socket(socket.AF_INET, socket.SOCK_STREAM);
                        s.connect((item['ip'], item['port']))
                        msg = Message(item['name'], None, None, data, 'config', None)
                        s.send(yaml.dump(msg))

                cliSock.send(yaml.dump(config))
                print "Config file after getting is : ",
                print config
        '''
        if(len(t) < 5):
                cliSock.send("NACK")
        else:
            i = 4
            neighbors = []
            while i < len(t):
                neighbors.add(t[i])
                i = i + 1
            ret = configfile.new_node(t[1],t[2], t[3], neighbors)
            cliSock.send(ret)
        '''
    elif(t[0] == 'remove_edge'):
        if(len(t) < 3):
            cliSock.send("NACK")
        else:
            ret = configfile.remove_edge(t[1], t[2])

            print remove_edge_from_config(t[1], t[2])

            print "Delete Auth new config ",
            print config
            print "Config copy after delettion is : ",
            print config_copy
            cliSock.send(ret)
    elif(t[0] == 'validate_userlist'):
        if (len(t)<2):
            cliSock.send("INVALID")
        else:
            returnstring="VALID"
            ret = t[1].split(",")
            for user in ret:
                if(user not in userList):
                    if(user == 'student' or user == 'faculty' or user == 'staff' or user == 'guest'):
                        pass
                    else:
                        returnstring="INVALID"
            cliSock.send(returnstring)
    elif(t[0] == 'test_cookie'):
        if(len(t) < 3):
            cliSock.send("NACK")
        else:
            res = test_cookie(t[1],t[2])
            cliSock.send(res)
    cliSock.close()
